#include "globals.h"
#include "errno.h"

#include "util/string.h"
#include "util/debug.h"

#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/mm.h"
#include "mm/page.h"
#include "mm/slab.h"
#include "mm/tlb.h"


#include "vm/vmmap.h"
#include "vm/shadow.h"
#include "vm/shadowd.h"
#include "fs/vnode.h"

#define SHADOW_SINGLETON_THRESHOLD 5

int shadow_count = 0; /* for debugging/verification purposes */
#ifdef __SHADOWD__
/*
 * number of shadow objects with a single parent, that is another shadow
 * object in the shadow objects tree(singletons)
 */
static int shadow_singleton_count = 0;
#endif

static slab_allocator_t *shadow_allocator;

static void shadow_ref(mmobj_t *o);
static void shadow_put(mmobj_t *o);
static int shadow_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite,
		pframe_t **pf);
static int shadow_fillpage(mmobj_t *o, pframe_t *pf);
static int shadow_dirtypage(mmobj_t *o, pframe_t *pf);
static int shadow_cleanpage(mmobj_t *o, pframe_t *pf);

static mmobj_ops_t shadow_mmobj_ops = { .ref = shadow_ref, .put = shadow_put,
		.lookuppage = shadow_lookuppage, .fillpage = shadow_fillpage,
		.dirtypage = shadow_dirtypage, .cleanpage = shadow_cleanpage };

/*
 * This function is called at boot time to initialize the
 * shadow page sub system. Currently it only initializes the
 * shadow_allocator object.
 */
void shadow_init() {
	shadow_allocator = slab_allocator_create("shadow_mmobj_ops",
			sizeof(mmobj_ops_t));
	KASSERT(NULL != shadow_allocator);
    dbg(DBG_VM,"(GRADING 3.a) shadow_allocator exists. \n");
	/*NOT_YET_IMPLEMENTED("VM: shadow_init");*/
}

/*
 * You'll want to use the shadow_allocator to allocate the mmobj to
 * return, then then initialize it. Take a look in mm/mmobj.h for
 * macros which can be of use here. Make sure your initial
 * reference count is correct.
 */
mmobj_t *
shadow_create() {
	mmobj_t* new_mmobj = NULL;
	new_mmobj = slab_obj_alloc(shadow_allocator);
	KASSERT(NULL != new_mmobj);
	mmobj_init(new_mmobj, &shadow_mmobj_ops);
	dbg(DBG_VM,"VM shadow_create: mmobj is initialized, initial refcount is %d. \n",new_mmobj->mmo_refcount);
	return new_mmobj;
	/*	NOT_YET_IMPLEMENTED("VM: shadow_create");
	return NULL;*/
}

/* Implementation of mmobj entry points: */

/*
 * Increment the reference count on the object.
 */
static void shadow_ref(mmobj_t *o) {
    if(o->mmo_refcount)
    {
		KASSERT(o && (0 < o->mmo_refcount) && (&shadow_mmobj_ops == o->mmo_ops));
		dbg(DBG_VM,"(GRADING 3.b) o and its operations and refcount are in position. \n");
    }
	KASSERT(o);
	KASSERT((0<=o->mmo_refcount));
	o->mmo_refcount++;

/*	NOT_YET_IMPLEMENTED("VM: shadow_ref");*/
}

/*
 * Decrement the reference count on the object. If, however, the
 * reference count on the object reaches the number of resident
 * pages of the object, we can conclude that the object is no
 * longer in use and, since it is a shadow object, it will never
 * be used again. You should unpin and uncache all of the object's
 * pages and then free the object itself.
 */
static void shadow_put(mmobj_t *o) {
	KASSERT(o && (0 < o->mmo_refcount) && (&shadow_mmobj_ops == o->mmo_ops));
    dbg(DBG_VM,"(GRADING 3.c) o and its operations and refcount are in position. \n");

	dbg(DBG_VM,"VM shadow_put begin\n");
	KASSERT(o);
	KASSERT(o->mmo_nrespages>=0);
	KASSERT(o->mmo_refcount>=o->mmo_nrespages);

	o->mmo_refcount--;

	if ((o->mmo_refcount-1) == o->mmo_nrespages){
		pframe_t *pf=NULL;

		/*unpin and uncache*/
		list_iterate_begin(&o->mmo_respages,pf,pframe_t,pf_olink){
			KASSERT(!pframe_is_dirty(pf));
			pframe_unpin(pf);
			pframe_free(pf);
		}list_iterate_end();
	}

	dbg(DBG_VM,"nrespages=%d, refcount=%d before free object\n",o->mmo_nrespages,o->mmo_refcount);
	KASSERT(0 == o->mmo_nrespages);
	KASSERT(1 == o->mmo_refcount);
	slab_obj_free(shadow_allocator,o);
	dbg(DBG_VM,"VM shadow_put exit\n");


/*	NOT_YET_IMPLEMENTED("VM: shadow_put");*/
}

/* This function looks up the given page in this shadow object. The
 * forwrite argument is true if the page is being looked up for
 * writing, false if it is being looked up for reading. This function
 * must handle all do-not-copy-on-not-write magic (i.e. when forwrite
 * is false find the first shadow object in the chain which has the
 * given page resident). copy-on-write magic (necessary when forwrite
 * is true) is handled in shadow_fillpage, not here. */
static int shadow_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite,
		pframe_t **pf) {
	dbg(DBG_VM,"VM shadow_lookuppage begin\n");
    KASSERT(NULL != pf);
    KASSERT(NULL != o);
	pframe_t *pfm=NULL;
    if (forwrite==0)
    {
	
	while(o->mmo_shadowed!=NULL)
	{
		list_iterate_begin(&o->mmo_respages,pfm,pframe_t,pf_olink){
			if (pfm->pf_pagenum==pagenum){
				*pf=pfm;
				dbg(DBG_VM,"forwrite ==false, page found, pagenum=%d\n",(*pf)->pf_pagenum);
				return 0;
			}
		}list_iterate_end();
		o=o->mmo_shadowed;
	}
	list_iterate_begin(&o->mmo_respages,pfm,pframe_t,pf_olink)
	{
			if (pfm->pf_pagenum==pagenum){
				*pf=pfm;
				dbg(DBG_VM,"forwrite ==false, page found, pagenum=%d\n",(*pf)->pf_pagenum);
				return 0;
			}
	}list_iterate_end();
	dbg(DBG_VM,"Do not find any corresponding page\n");
    	return -1;
    }
    else 
    {
	
	pframe_get(o,pagenum,pf);
	
	return 0;
    }
    return -1;
/*	NOT_YET_IMPLEMENTED("VM: shadow_lookuppage");
	return 0;*/
}

/* As per the specification in mmobj.h, fill the page frame starting
 * at address pf->pf_addr with the contents of the page identified by
 * pf->pf_obj and pf->pf_pagenum. This function handles all
 * copy-on-write magic (i.e. if there is a shadow object which has
 * data for the pf->pf_pagenum-th page then we should take that data,
 * if no such shadow object exists we need to follow the chain of
 * shadow objects all the way to the bottom object and take the data
 * for the pf->pf_pagenum-th page from the last object in the chain). */
static int shadow_fillpage(mmobj_t *o, pframe_t *pf) {
	
	KASSERT(NULL != pf);
	KASSERT(NULL != o);

	KASSERT(pframe_is_busy(pf));
    dbg(DBG_VM,"(GRADING 3.d) pf is set busy\n");

    KASSERT(!pframe_is_pinned(pf));
    dbg(DBG_VM,"(GRADING 3.d) pframe is pinned\n");

	int flag=0;
	dbg(DBG_VM,"entered shadow_fillpage \n");
	pframe_t *pfm=NULL;
	list_iterate_begin(&o->mmo_un.mmo_bottom_obj->mmo_respages,pfm,pframe_t,pf_olink){
			if (pfm->pf_pagenum==pf->pf_pagenum){
				flag=1;
				dbg(DBG_ELF,"bottom object has pframe\n");
			}
	}list_iterate_end();
	if(flag==0)
	{
		dbg(DBG_ELF,"bottom object is NULL\n");
		pframe_get(o->mmo_un.mmo_bottom_obj,pf->pf_pagenum,&pfm);
	}
	o=o->mmo_shadowed;
	while(o->mmo_shadowed!=NULL)
	{
		list_iterate_begin(&o->mmo_respages,pfm,pframe_t,pf_olink){
			if (pfm->pf_pagenum==pf->pf_pagenum){
				memcpy((void*)((size_t)pf->pf_addr+PAGE_OFFSET(pf->pf_addr)),(void*)((size_t)pfm->pf_addr+PAGE_OFFSET(pfm->pf_addr)),PAGE_SIZE - PAGE_OFFSET(pf->pf_addr));
				dbg(DBG_ELF,"shadow_fillpage success 2level\n");
				return 0;
			}
		}list_iterate_end();
		o=o->mmo_shadowed;
	}
	list_iterate_begin(&o->mmo_respages,pfm,pframe_t,pf_olink){
			if (pfm->pf_pagenum==pf->pf_pagenum){
				memcpy((void*)((size_t)pf->pf_addr+PAGE_OFFSET(pf->pf_addr)),(void*)((size_t)pfm->pf_addr+PAGE_OFFSET(pfm->pf_addr)),PAGE_SIZE - PAGE_OFFSET(pf->pf_addr));
				dbg(DBG_ELF,"shadow_fillpage success 1level\n");
				return 0;
			}
		}list_iterate_end();
	dbg(DBG_ELF,"shadow_fillpage failed\n");
	return -1;

/*	NOT_YET_IMPLEMENTED("VM: shadow_fillpage");
	return 0;*/
}

/* These next two functions are not difficult. */

static int shadow_dirtypage(mmobj_t *o, pframe_t *pf) {

    KASSERT(NULL != pf);
    KASSERT(NULL != o);

	  if(!(pframe_is_dirty(pf)))
		pframe_set_dirty(pf);
	
	return 0;


/*	NOT_YET_IMPLEMENTED("VM: shadow_dirtypage");
	return -1;*/
}

static int shadow_cleanpage(mmobj_t *o, pframe_t *pf) {
dbg(DBG_VMMAP,"ENTERED SHADOW_CLEANPAGE\n");
    KASSERT(NULL != pf);
    KASSERT(NULL != o);

	pframe_t *pfm;

	int retv;
	if(!(retv = pframe_get(o, pf->pf_pagenum, &pfm))){
		memcpy((void*)((size_t)pfm->pf_addr+PAGE_OFFSET(pfm->pf_addr)),(void*)((size_t)pf->pf_addr+PAGE_OFFSET(pf->pf_addr)),(size_t)(PAGE_SIZE-PAGE_OFFSET(pf->pf_addr)));
	}
	return retv;

/*	NOT_YET_IMPLEMENTED("VM: shadow_cleanpage");
	return -1;*/
}
